package ClientServer(
        Client(..), Server(..), ClientServer(..), mkRequestResponseBuffer,
        mkSizedRequestResponseBuffer, mkResponseBuffer, mkRequestBuffer,
        mkRequestResponseBuffer1, joinServersBC, joinServers, splitServer,
        fifosToServer, fifosToClient, fifosToClientServer,
        toGPClient, toGPServer)
    where
import qualified Vector
import List
import FIFO
import Counter
import Connectable
import GetPut
import Clocks

--@ \subsubsection{ClientServer}
--@ \index{ClientServer@\te{ClientServer} (package)|textbf}
--@
--@ The interfaces \te{Client} and \te{Server} can be used for
--@ modules that have a request-response type of interface
--@ (e.g. a RAM).
--@ The server accepts requests and generates responses, and
--@ vice versa for the client.
--@ There are no assumptions about how many (if any) responses
--@ a request will generate.

--@ \index{Client@\te{Client} (interface)|textbf}
--@ \begin{libverbatim}
--@ interface Client #(type a, type b);
--@     interface Get#(a) request();
--@     interface Put#(b) response();
--@ endinterface: Client
--@ \end{libverbatim}
interface Client a b =
    request  :: Get a
    response :: Put b

--@ \index{Server@\te{Server} (interface)|textbf}
--@ \begin{libverbatim}
--@ interface Server #(type a, type b);
--@     interface Put#(a) request();
--@     interface Get#(b) response();
--@ endinterface: Server
--@ \end{libverbatim}
interface Server a b =
    request  :: Put a
    response :: Get b

--@ A \te{Client} can be connected to a \te{Server}
--@ and vice versa.
--@ \begin{libverbatim}
--@ instance Connectable #(Client#(a, b), Server#(a, b));
--@ instance Connectable #(Server#(a, b), Client#(a, b));
--@ \end{libverbatim}
instance Connectable (Client a b) (Server a b)
   where
    mkConnection :: (IsModule m c ) => Client a b -> Server a b -> m Empty
    mkConnection c s =
       module
          rules
            "ClientServerRequest":when True
             ==> action
                    x :: a <- c.request.get
                    s.request.put x
            "ClientServerResponse":when True
             ==> action
                    x :: b <- s.response.get
                    c.response.put x

instance Connectable (Server a b) (Client a b)
   where
    mkConnection s c = mkConnection c s

instance (Bits a sa, Bits b sb) => ClockConv (Client a b) where
    mkConverter :: (Prelude.IsModule m c) => Integer -> Client a b -> m (Client a b)
    mkConverter d cluses =
        module
            req  <- mkConverter d cluses.request
            resp <- mkConverter d cluses.response

            interface
              request  = req
              response = resp

instance (Bits a sa, Bits b sb) => ClockConv (Server a b) where
    mkConverter :: (Prelude.IsModule m c) => Integer -> Server a b -> m (Server a b)
    mkConverter d cluses =
        module
            req  <- mkConverter d cluses.request
            resp <- mkConverter d cluses.response

            interface
              request  = req
              response = resp

--@ \lineup
--@ \begin{libverbatim}
--@ typedef Tuple2 #(Client#(a, b), Server#(a, b)) ClientServer #(type a, type b);
--@ \end{libverbatim}
type ClientServer a b = (Client a b, Server a b)

--@ Create a buffer that just passes requests and responses between the
--@ two generated interfaces.
--@ \index{mkRequestResponseBuffer@\te{mkRequestResponseBuffer} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkRequestResponseBuffer(ClientServer#(a, b))
--@   provisos (Bits#(a, sa), Bits#(b, sb));
--@ \end{libverbatim}
mkRequestResponseBuffer :: (IsModule m c , Bits a sa, Bits b sb) => m (ClientServer a b)
mkRequestResponseBuffer =
    module
        (qget, qput) :: (Get a, Put a) <- mkGetPut
        (sget, sput) :: (Get b, Put b) <- mkGetPut
        let c = interface Client
                    request  = qget
                    response = sput
            s = interface Server
                    request  = qput
                    response = sget
        interface (c, s)

--@ Create a buffer that just passes requests and responses between the
--@ two generated interfaces.  Uses half the flops of
--@ \te{mkRequestResponseBuffer}, but also has half the throughput.
--@ \index{mkRequestResponseBuffer1@\te{mkRequestResponseBuffer1} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkRequestResponseBuffer1(ClientServer#(a, b))
--@   provisos (Bits#(a, sa), Bits#(b, sb));
--@ \end{libverbatim}
mkRequestResponseBuffer1 :: (IsModule m c , Bits a sa, Bits b sb) => m (ClientServer a b)
mkRequestResponseBuffer1 =
    module
        (qget, qput) :: (Get a, Put a) <- mkGPFIFO1
        (sget, sput) :: (Get b, Put b) <- mkGPFIFO1
        let c = interface Client
                    request  = qget
                    response = sput
            s = interface Server
                    request  = qput
                    response = sget
        interface (c, s)

--@ The same, using sized FIFOs.
--@ \index{mkSizedRequestResponseBuffer@\te{mkSizedRequestResponseBuffer} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkSizedRequestResponseBuffer#(Integer sz)(ClientServer#(a, b))
--@   provisos (Bits#(a, sa), Bits#(b, sb));
--@ \end{libverbatim}
mkSizedRequestResponseBuffer :: (IsModule m c , Bits a sa, Bits b sb) => Integer -> m (ClientServer a b)
mkSizedRequestResponseBuffer sz =
    module
        (qget, qput) :: (Get a, Put a) <- mkGPSizedFIFO sz
        (sget, sput) :: (Get b, Put b) <- mkGPSizedFIFO sz
        let c = interface Client
                    request  = qget
                    response = sput
            s = interface Server
                    request  = qput
                    response = sget
        interface (c, s)


--@ Create a new server with buffered requests.
--@ \index{mkRequestBuffer@\te{mkRequestBuffer} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkRequestBuffer#(Server#(a, b) s)(Server#(a, b))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
mkRequestBuffer :: (IsModule m c , Bits a sa) => Server a b -> m (Server a b)
mkRequestBuffer s =
    module
        (qget, qput) :: (Get a, Put a) <- mkGetPut
        rules
              "mkRequestBuffer" : when True ==>
                 action
                   x :: a <- qget.get
                   s.request.put x

        interface -- Server
                    request  = qput
                    response = s.response


--@ Create a new server with buffered responses.
--@ \index{mkResponseBuffer@\te{mkResponseBuffer} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkResponseBuffer#(Server#(a, b) s)(Server#(a, b))
--@   provisos (Bits#(b, sb));
--@ \end{libverbatim}
mkResponseBuffer :: (IsModule m c , Bits b sb) => Server a b -> m (Server a b)
mkResponseBuffer s =
    module
        (sget, sput) :: (Get b, Put b) <- mkGetPut
        rules
              "mkResponseBuffer" : when True ==>
                 action
                   x :: b <- s.response.get
                   sput.put x

        interface -- Server
                    request  = s.request
                    response = sget

--@ Join a list of servers to one server.  All incoming requests are broadcasted
--@ all the servers and all responses are merged.
--@ The function introduces a one cycle latency on the response.
--@ \index{joinServersBC@\te{joinServersBC} (function)|textbf}
--@ \begin{libverbatim}
--@ module joinServersBC#(List#(Server#(a, b)) ifs)(Server#(a, b))
--@   provisos (Bits#(b, sb));
--@ \end{libverbatim}
joinServersBC :: (IsModule m c , Bits b sb) => List (Server a b) -> m (Server a b)
joinServersBC ifs =
  module
    (sget, sput) :: (Get b, Put b) <- mkGetPut
    let rl s =
            rules
              "joinServersBC":
                when True ==>
                 action
                   x <- (s :: Server a b).response.get
                   sput.put x
    addRules $ foldr (<+>) (rules {}) (map rl ifs)
    interface -- Server
            request =
             interface Put
              put req = joinActions (map (\ i -> (i :: Server a b).request.put req) ifs)
            response = sget

--@ Join a list of servers to one server.  All incoming requests are sent to
--@ a selected subset of the servers and all responses are merged.
--@ The selection is my a function that can transform the request type
--@ while testing if it should be sent on.
--@ The function introduces a one cycle latency on the response.
--@ \index{joinServers@\te{joinServers} (function)|textbf}
--@ \begin{libverbatim}
--@ module joinServers#( List#(Tuple2 #(a -> Maybe#(a'),
--@                      Server#(a', b))) ifs)(Server#(a, b))
--@   provisos (Bits#(b, sb));
--@ \end{libverbatim}
joinServers ::  (IsModule m c , Bits b sb) => List (a -> Maybe a', Server a' b) -> m (Server a b)
joinServers ifs =
  module
    (sget, sput) :: (Get b, Put b) <- mkGetPut
    let rl (_, s) =
            rules
              "joinServers":
                when True ==>
                  action
                   x <- (s :: Server a' b).response.get
                   sput.put x
        send req (f, s) =
            case f req of
            Nothing -> noAction
            Just req' -> (s :: Server a' b).request.put req'
    addRules $ foldr (<+>) (rules {}) (map rl ifs)
    interface -- Server
            request =
             interface Put
              put req = joinActions (map (send req) ifs)
            response = sget

type MaxLat = 8        -- XXX This is just wrong
--@ Split a server into a number of identical servers.
--@ The integer argument specifies how many outstanding requests
--@ a returned server may have.  This number should be
--@ the latency of the argument server to sustain full bandwidth.
--@ (A small number still works, as does a larger number.)
--@ \index{splitServer@\te{splitServer} (function)|textbf}
--@ \begin{libverbatim}
--@ module splitServer#(Integer lat, Server#(a, b) serv)(Vector#(n, Server#(a, b)))
--@   provisos (Bits#(b, sb), Log#(n, ln));
--@ \end{libverbatim}
splitServer :: (IsModule m c , Bits b sb, Log n ln) =>
               Integer -> Server a b -> m (Vector.Vector n (Server a b))
splitServer lat serv =
  module
    tags :: FIFO (Bit ln) <- mkSizedFIFO lat
    let mkServ :: (Bit ln) -> m (Server a b)
        mkServ i =
          module
            out :: FIFO b <- mkSizedFIFO lat
            cnt :: Counter MaxLat <- mkCounter (fromInteger lat)
            rules
                when tags.first == i
                 ==> action
                        tags.deq
                        x <- serv.response.get
                        out.enq x
            interface Server
              request =
                interface Put
                  put req =
                        action
                            serv.request.put req
                            tags.enq i
                            cnt.down
                    when cnt.value > 0
              response =
                interface Get
                  get = do
                            out.deq
                            cnt.up
                            return out.first
    Vector.mapM (mkServ ∘ fromInteger) Vector.genList

--@ fifosToServer

fifosToServer :: FIFO rq -> FIFO rs -> Server rq rs
fifosToServer rqf rsf =
    interface Server
       request  = fifoToPut rqf
       response = fifoToGet rsf

fifosToClient :: FIFO rq -> FIFO rs -> Client rq rs
fifosToClient rqf rsf =
    interface Client
       request  = fifoToGet rqf
       response = fifoToPut rsf

fifosToClientServer :: FIFO rq -> FIFO rs -> ClientServer rq rs
fifosToClientServer rqf rsf = (fifosToClient rqf rsf, fifosToServer rqf rsf)

-- toGPClient, toGPServer

toGPClient :: (ToGet rq_ifc rq, ToPut rs_ifc rs) =>
              rq_ifc -> rs_ifc -> Client rq rs
toGPClient rqi rsi =
    interface Client
        request = toGet rqi
        response = toPut rsi

toGPServer :: (ToPut rq_ifc rq, ToGet rs_ifc rs) =>
              rq_ifc -> rs_ifc -> Server rq rs
toGPServer rqi rsi =
    interface Server
        request = toPut rqi
        response = toGet rsi

